#ifndef SANDBOX_SDK_MATH_VECTOR4F_HPP
#define SANDBOX_SDK_MATH_VECTOR4F_HPP

#include "vector4_details.h"
#include <type_traits>
namespace sandbox
{
	namespace sdk
	{
		//forward declarations
		struct Matrix4f;
		struct Matrix3f;
		struct Quaternion;
		
		ALIGN_BEG(16) struct Vector4f {
			Vector4f(){}
			constexpr Vector4f(float a):value(_mm_set1_ps(a)){}
			constexpr Vector4f(float x, float y, float z, float w):value(_mm_set_ps(w,x,y,z)){}
			
			Vector4f& operator +=(const Vector4f  x)	{value=_mm_add_ps(value, x.value);return *this;}
			Vector4f& operator -=(const Vector4f  x)	{value=_mm_sub_ps(value, x.value);return *this;}
			Vector4f& operator *=(const Vector4f  x)	{value=_mm_mul_ps(value, x.value);return *this;}

			void setW(Vector4f  src){value=_mm_move_ss(value, src.value);}
					
			Vector4f abs()const//bit magic - clear upper bit
			{return Vector4f((__m128)_mm_srli_epi32(_mm_slli_epi32((__m128i)value,1),1));}
			Vector4f inv()const
			{return Vector4f(_mm_rcp_ps(value));}
			Vector4f prod(const Vector4f  a)const
			{
				register __m128 mm1=_mm_mul_ps(value,a.value);
#ifdef __SSE3__
				mm1 = _mm_hadd_ps(mm1,mm1);
				mm1 = ::sandbox::sdk::detail::shuffle<__m128, 0,2,1,3>(mm1);
				mm1 = _mm_hadd_ps(mm1,mm1);
#else
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,1,0,3,2>(mm1);
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,2,2,0,0>(mm1);
#endif
				return Vector4f(mm1);
			}
			Vector4f len()const
			{
				register __m128 mm1=_mm_mul_ps(value,value);
#ifdef __SSE3__
				mm1 = _mm_hadd_ps(mm1,mm1);
				mm1 = ::sandbox::sdk::detail::shuffle<__m128, 0,2,1,3>(mm1);
				mm1 = _mm_hadd_ps(mm1,mm1);
#else
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,1,0,3,2>(mm1);
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,2,2,0,0>(mm1);
#endif
				return Vector4f((__m128)_mm_shuffle_epi32((__m128i)_mm_sqrt_ss(mm1),0)));
			}
			Vector4f invlen()const
			{
				register __m128 mm1=_mm_mul_ps(value,value);
#ifdef __SSE3__
				mm1 = _mm_hadd_ps(mm1,mm1);
				mm1 = ::sandbox::sdk::detail::shuffle<__m128, 0,2,1,3>(mm1);
				mm1 = _mm_hadd_ps(mm1,mm1);
#else
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,1,0,3,2>(mm1);
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,2,2,0,0>(mm1);
#endif
				return Vector4f(_mm_rsqrt_ps(mm1));
			}
			Vector4f norm()const
			{
				register __m128 mm1=_mm_mul_ps(value,value);
#ifdef __SSE3__
				mm1 = _mm_hadd_ps(mm1,mm1);
				mm1 = ::sandbox::sdk::detail::shuffle<__m128, 0,2,1,3>(mm1);
				mm1 = _mm_hadd_ps(mm1,mm1);
#else				
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,1,0,3,2>(mm1);
				mm1 = _mm_add_ps(mm1, 
					::sandbox::sdk::detail::shuffle<__m128,2,2,0,0>(mm1);
				mm1 = _mm_rsqrt_ps(mm1);
				mm1 = _mm_mul_ps(mm1, value);
#endif
				return Vector4f(mm1);
			}
			template <int w, int x=w, int y=w, int z=w> Vector4f shuffle()const
			{return Vector4f(detail::shuffle<w,x,y,z>(value);}
			Vector4f sqrt()const
			{return Vector4f(_mm_sqrt_ps(value));}
			Vector4f invsqrt()const
			{return Vector4f(_mm_rsqrt_ps(value));}
			Vector4f sqrt1()const
			{return Vector4f(_mm_sqrt_ss(value));}
			Vector4f invsqrt1()const
			{return Vector4f(_mm_rsqrt_ss(value));}

			template<int i>	float get()const
			{
				STATIC_ASSERT(i>=0 && i<4);
				return __builtin_ia32_vec_ext_v4sf(value,0);
			}

			int mask()const{return _mm_movemask_ps(value);}
			enum { XMask(2), YMask(4), ZMask(8), WMask(1), XYZMask(0xE) }

			const float * data()const {return m_value;}

			constexpr Vector4f(__m128 v):value(v){}
			__v4sf value;
			
		} ALIGN_END(16);
		
		static_assert(std::is_pod<Vector4f>::value==true);
		static_assert(sizeof(Vector4f)==16);

		const Vector4f operator +(const Vector4f  a,const Vector4f  b)
		{return Vector4f(_mm_add_ps(a.value,b.value));}
		const Vector4f operator -(const Vector4f  a,const Vector4f  b)
		{return Vector4f(_mm_sub_ps(a.value,b.value));}
		const Vector4f operator *(const Vector4f  a,const Vector4f  b)
		{return Vector4f(_mm_mul_ps(a.value,b.value));}
		const Vector4f max(const Vector4f  a,const Vector4f  b)
		{return Vector4f(_mm_max_ps(a.value,b.value));}
		const Vector4f min(const Vector4f  a,const Vector4f  b)
		{return Vector4f(_mm_min_ps(a.value,b.value));}
		const Vector4f cross(const Vector4f  a,const Vector4f  b)
		{
			register __m128 v1=_mm_mul_ps(
				::sandbox::sdk::detail::shuffle<__m128,0,2,3,1>(a.value),
				::sandbox::sdk::detail::shuffle<__m128,0,3,1,2>(b.value));
			register __m128 v2=_mm_mul_ps(
				::sandbox::sdk::detail::shuffle<__m128,0,2,3,1>(b.value),
				::sandbox::sdk::detail::shuffle<__m128,0,3,1,2>(a.value));
			return Vector4f(_mm_sub_ps(v1,v2));
		}
	}
}
#endif //SANDBOX_SDK_MATH_VECTOR4F_HPP
